
// A direct port of libc's ns.c file.
package service

import (
	"syscall"
	"syscall/async"
)

func ConvertID(s string) (result uint32) {
	bytes := [4]byte{ ' ', ' ', ' ', ' ' }

	switch len(s) {
	default:
		panic("Invalid input to service.ConvertID()")
	case 4:
		bytes[3] = byte(s[3])
		fallthrough
	case 3:
		bytes[2] = byte(s[2])
		fallthrough
	case 2:
		bytes[1] = byte(s[1])
		fallthrough
	case 1:
		bytes[0] = byte(s[0])
		fallthrough
	case 0:
	}

	return uint32(bytes[0]) | uint32(bytes[1]) << 8 | uint32(bytes[2]) << 16 | uint32(bytes[3]) << 24
}

func Register(service uint32) syscall.Errno {
	exch := nsSession().ExchangeBegin()
	if exch == nil {
		panic("Something is horribly wrong. RUN!!")
	}
	defer exch.End()

	return exch.ConnectToMe(syscall.Arg(service), 0, 0, nil)
}

func Connect(mgmt async.ExchMgmt, service uint32, arg2, arg3 syscall.Arg) (syscall.Errno, *async.Session) {
	exch := nsSession().ExchangeBegin()
	if exch == nil {
		panic("Something is horribly wrong. RUN!!")
	}
	defer exch.End()

	rc, sess := exch.ConnectMeTo(mgmt, syscall.Arg(service), arg2, arg3)

	if rc == syscall.EOK {
		// FIXME Ugly hack to work around limitation of implementing
		// parallel exchanges using multiple connections. Shift out
		// first argument for non-initial connections.

		sess.SetArgs(arg2, arg3, 0)
	}
	return rc, sess
}

func ConnectBlocking(mgmt async.ExchMgmt, service uint32, arg2, arg3 syscall.Arg) (syscall.Errno, *async.Session) {
	exch := nsSession().ExchangeBegin()
	if exch == nil {
		panic("Something is horribly wrong. RUN!!")
	}
	defer exch.End()

	rc, sess := exch.ConnectMeToBlocking(mgmt, syscall.Arg(service), arg2, arg3)

	if rc == syscall.EOK {
		// FIXME Ugly hack to work around limitation of implementing
		// parallel exchanges using multiple connections. Shift out
		// first argument for non-initial connections.

		sess.SetArgs(arg2, arg3, 0)
	}
	return rc, sess
}

func Bind(service uint32, arg1, arg2, arg3 syscall.Arg, callback async.CallHandler) (rc syscall.Errno, s *async.Session) {
	rc, sess := ConnectBlocking(async.EXCHANGE_SERIALIZE, service, 0, 0)
	if rc != syscall.EOK {
		return rc, nil
	}
	defer func(){
		if s == nil {
			sess.HangUp()
		}
	}()

	// Request callback connection.
	exch := sess.ExchangeBegin()
	if exch == nil {
		return syscall.EPARTY, nil
	}
	defer exch.End()

	rc = exch.ConnectToMe(arg1, arg2, arg3, callback)

	if rc != syscall.EOK {
		return rc, nil
	}

	return syscall.EOK, sess
}
